#include "sbrush_spectral_model_player.h"

using namespace sbrush;
using namespace dag;

SpectralModelPlayer::SpectralModelPlayer()
:	m_Model(NULL),
	m_CurrentFrame(0),
	m_SynthBuffer(NULL),
	m_SynthBufferSize(0),
	m_SamplesInBuffer(0)
{
}

SpectralModelPlayer::~SpectralModelPlayer() throw() {
	AudioManager::removeSource(this);
	delete[] m_SynthBuffer;
}

void SpectralModelPlayer::setModel(const SpectralModel& model) {
	ScopedLock sl(m_AudioMutex);
	m_Model = &model;
	m_CurrentFrame = 0;
	m_SamplesInBuffer = 0;
	
	m_Synthesizer.init(*model.header(),
					   (int)AudioManager::getSampleRate());
	delete[] m_SynthBuffer;
	m_SynthBuffer = NULL;
	m_SynthBufferSize = m_Synthesizer.samplesPerSmsFrame();
	if (m_SynthBufferSize > 0) {
		m_SynthBuffer = new float[m_SynthBufferSize];
	}
}

void SpectralModelPlayer::start() {
	AudioManager::addSource(this);
}

void SpectralModelPlayer::stop() {
	AudioManager::removeSource(this);
}

int SpectralModelPlayer::playHead() const {
	ScopedLock sl(m_AudioMutex);
	return m_CurrentFrame;
}

void SpectralModelPlayer::seek(int pos, Whence whence) {
	if (m_Model == NULL) {
		return;
	}
	
	ScopedLock sl(m_AudioMutex);
	switch (whence) {
		case Whence_Beginning:
			if (pos > 0) {
				m_CurrentFrame = pos;
			} else {
				m_CurrentFrame = 0;
			}
			break;
		case Whence_End:
			if (m_Model->getNumFrames() > (-pos)) {
				m_CurrentFrame = (int)m_Model->getNumFrames() + pos;
			} else {
				m_CurrentFrame = 0;
			}
			break;
		case Whence_Current:
			if (pos < 0) {
				if (m_CurrentFrame > (-pos)) {
					m_CurrentFrame += pos;
				} else {
					m_CurrentFrame = 0;
				}
			} else {
				m_CurrentFrame += pos;
			}
			break;
	}
}

int SpectralModelPlayer::length() const {
	if (m_Model != NULL) {
		return m_Model->getNumFrames();
	}
	return 0;
}
	

void SpectralModelPlayer::addAudio(float** buffer, 
								   size_t numChannels, 
								   size_t numFrames) {
	if (m_Model == NULL) {
		return;
	}
	bool hasFinished = false;
	{
		ScopedLock sl(m_AudioMutex);
		size_t putPos = 0;
		putPos = addAudioInternal(buffer, numChannels, numFrames, putPos);
		while (putPos != numFrames) {
			
			if ((m_CurrentFrame + 1) >= m_Model->getNumFrames()) {
				hasFinished = true;
				break;
			}
			m_Synthesizer.setFrame(m_Model->getFrame(m_CurrentFrame));
			m_Synthesizer.addAudio(&m_SynthBuffer, 1, m_SynthBufferSize);
			m_CurrentFrame++;
			m_SamplesInBuffer = m_SynthBufferSize;
			putPos = addAudioInternal(buffer, numChannels, numFrames, putPos);
		}
	}
	
	if (hasFinished) {
		finished();
		stop();
	}
}

size_t SpectralModelPlayer::addAudioInternal(float** buffer, 
											 size_t numChannels,
											 size_t numFrames,
											 size_t bufferOffset) {
	if (m_SamplesInBuffer > 0) {
		size_t getPos = m_SynthBufferSize - m_SamplesInBuffer;
		size_t num2Put = 0;
		size_t numLeft = numFrames - bufferOffset;
		if (m_SamplesInBuffer > numLeft) {
			num2Put = numLeft;
		} else {
			num2Put = m_SamplesInBuffer;
		}
		for (size_t channel = 0; channel < numChannels; channel++) {
			memcpy(&buffer[channel][bufferOffset],
				   &m_SynthBuffer[getPos], 
				   num2Put * sizeof(float));
		}
		bufferOffset += num2Put;
		m_SamplesInBuffer -= num2Put;
	}
	
	return bufferOffset;
}
